use crate::entities::{product, product_translation, product_resource, resource};
use crate::errors::AppError;
use sea_orm::*;
use uuid::Uuid;

pub struct ProductService;

impl ProductService {
    pub async fn find_published_products(
        db: &DatabaseConnection,
        language_code: Option<&str>,
        category_id: Option<Uuid>,
        brand_id: Option<Uuid>,
        limit: Option<u64>,
        offset: Option<u64>,
    ) -> Result<Vec<(product::Model, Option<product_translation::Model>)>, AppError> {
        let mut query = product::Entity::find()
            .filter(product::Column::Status.eq("published"))
            .find_with_related(product_translation::Entity);
        
        if let Some(lang) = language_code {
            query = query.filter(product_translation::Column::LanguageCode.eq(lang));
        }
        
        if let Some(cat_id) = category_id {
            query = query.filter(product::Column::CategoryId.eq(cat_id));
        }
        
        if let Some(brand) = brand_id {
            query = query.filter(product::Column::BrandId.eq(brand));
        }
        
        if let Some(limit_val) = limit {
            query = query.limit(limit_val);
        }
        
        if let Some(offset_val) = offset {
            query = query.offset(offset_val);
        }
        
        let products = query.all(db).await?;
        // Convert Vec<(Model, Vec<Model>)> to Vec<(Model, Option<Model>)>
        let result = products.into_iter().map(|(product, translations)| {
            (product, translations.into_iter().next())
        }).collect();
        Ok(result)
    }

    pub async fn find_product_by_sku(
        db: &DatabaseConnection,
        sku: &str,
        language_code: Option<&str>,
    ) -> Result<Option<(product::Model, Option<product_translation::Model>)>, AppError> {
        let mut query = product::Entity::find()
            .filter(product::Column::Sku.eq(sku))
            .filter(product::Column::Status.eq("published"))
            .find_with_related(product_translation::Entity);
        
        if let Some(lang) = language_code {
            query = query.filter(product_translation::Column::LanguageCode.eq(lang));
        }
        
        let products = query.all(db).await?;
        if let Some((product, translations)) = products.into_iter().next() {
            Ok(Some((product, translations.into_iter().next())))
        } else {
            Ok(None)
        }
    }

    pub async fn find_all_products(
        db: &DatabaseConnection,
        language_code: Option<&str>,
        status: Option<&str>,
        category_id: Option<Uuid>,
        brand_id: Option<Uuid>,
        limit: Option<u64>,
        offset: Option<u64>,
    ) -> Result<Vec<(product::Model, Option<product_translation::Model>)>, AppError> {
        let mut query = product::Entity::find().find_with_related(product_translation::Entity);
        
        if let Some(lang) = language_code {
            query = query.filter(product_translation::Column::LanguageCode.eq(lang));
        }
        
        if let Some(status_filter) = status {
            query = query.filter(product::Column::Status.eq(status_filter));
        }
        
        if let Some(cat_id) = category_id {
            query = query.filter(product::Column::CategoryId.eq(cat_id));
        }
        
        if let Some(brand) = brand_id {
            query = query.filter(product::Column::BrandId.eq(brand));
        }
        
        if let Some(limit_val) = limit {
            query = query.limit(limit_val);
        }
        
        if let Some(offset_val) = offset {
            query = query.offset(offset_val);
        }
        
        let products = query.all(db).await?;
        // Convert Vec<(Model, Vec<Model>)> to Vec<(Model, Option<Model>)>
        let result = products.into_iter().map(|(product, translations)| {
            (product, translations.into_iter().next())
        }).collect();
        Ok(result)
    }

    pub async fn find_product_by_id(
        db: &DatabaseConnection,
        id: Uuid,
        language_code: Option<&str>,
    ) -> Result<Option<(product::Model, Option<product_translation::Model>)>, AppError> {
        let mut query = product::Entity::find_by_id(id).find_with_related(product_translation::Entity);
        
        if let Some(lang) = language_code {
            query = query.filter(product_translation::Column::LanguageCode.eq(lang));
        }
        
        let products = query.all(db).await?;
        if let Some((product, translations)) = products.into_iter().next() {
            Ok(Some((product, translations.into_iter().next())))
        } else {
            Ok(None)
        }
    }

    pub async fn create_product(
        db: &DatabaseConnection,
        product_data: product::ActiveModel,
    ) -> Result<product::Model, AppError> {
        let product = product_data.insert(db).await?;
        Ok(product)
    }

    pub async fn update_product(
        db: &DatabaseConnection,
        id: Uuid,
        product_data: product::ActiveModel,
    ) -> Result<Option<product::Model>, AppError> {
        // First check if the product exists
        let existing = product::Entity::find_by_id(id).one(db).await?;
        if existing.is_none() {
            return Ok(None);
        }
        
        // Update the product
        let product = product_data.update(db).await?;
        Ok(Some(product))
    }

    pub async fn delete_product(
        db: &DatabaseConnection,
        id: Uuid,
    ) -> Result<bool, AppError> {
        let result = product::Entity::delete_by_id(id).exec(db).await?;
        Ok(result.rows_affected > 0)
    }

    pub async fn create_product_translation(
        db: &DatabaseConnection,
        translation_data: product_translation::ActiveModel,
    ) -> Result<product_translation::Model, AppError> {
        let translation = translation_data.insert(db).await?;
        Ok(translation)
    }

    pub async fn update_product_translation(
        db: &DatabaseConnection,
        id: Uuid,
        translation_data: product_translation::ActiveModel,
    ) -> Result<Option<product_translation::Model>, AppError> {
        // First check if the translation exists
        let existing = product_translation::Entity::find_by_id(id).one(db).await?;
        if existing.is_none() {
            return Ok(None);
        }
        
        // Update the translation
        let translation = translation_data.update(db).await?;
        Ok(Some(translation))
    }

    pub async fn delete_product_translation(
        db: &DatabaseConnection,
        id: Uuid,
    ) -> Result<bool, AppError> {
        let result = product_translation::Entity::delete_by_id(id).exec(db).await?;
        Ok(result.rows_affected > 0)
    }

    pub async fn get_product_resources(
        db: &DatabaseConnection,
        product_id: Uuid,
        language_code: Option<&str>,
    ) -> Result<Vec<(product_resource::Model, resource::Model)>, AppError> {
        let mut query = product_resource::Entity::find()
            .filter(product_resource::Column::ProductId.eq(product_id))
            .find_with_related(resource::Entity);
        
        if let Some(lang) = language_code {
            query = query.filter(product_resource::Column::LanguageCode.eq(lang));
        }
        
        let results = query.all(db).await?;
        let resources = results.into_iter().flat_map(|(product_res, resources)| {
            resources.into_iter().map(move |resource| (product_res.clone(), resource))
        }).collect();
        Ok(resources)
    }

    pub async fn get_published_by_id(
        db: &DatabaseConnection,
        id: Uuid,
        language_code: Option<&str>,
    ) -> Result<crate::models::Product, AppError> {
        let product = Self::find_product_by_id(db, id, language_code).await?
            .ok_or_else(|| AppError::NotFound {
                entity: "Product".to_string(),
                id,
            })?;

        // Check if product is published
        if product.0.status != "published" {
            return Err(AppError::NotFound {
                entity: "Product".to_string(),
                id,
            });
        }

        let translations = if let Some(translation) = product.1 {
            vec![crate::models::ProductTranslation {
                id: translation.id,
                product_id: translation.product_id,
                language_code: translation.language_code,
                name: translation.name,
                description: translation.description,
                short_description: translation.short_description,
                meta_title: translation.meta_title,
                meta_description: translation.meta_description,
            }]
        } else {
            vec![]
        };

        Ok(crate::models::Product {
            id: product.0.id,
            sku: product.0.sku,
            price: product.0.price,
            compare_price: product.0.compare_price,
            cost_price: product.0.cost_price,
            track_inventory: product.0.track_inventory,
            weight: product.0.weight,
            status: crate::models::PublicationStatus::from_str(&product.0.status)
                .map_err(|_| AppError::Validation("Invalid publication status".to_string()))?,
            featured_image_url: product.0.featured_image_url,
            published_at: product.0.published_at,
            created_at: product.0.created_at,
            updated_at: product.0.updated_at,
            translations,
            category_id: product.0.category_id,
            brand_id: product.0.brand_id,
        })
    }

    pub async fn list_published(
        db: &DatabaseConnection,
        options: crate::models::FilterOptions,
    ) -> Result<crate::models::PaginatedResponse<crate::models::Product>, AppError> {
        let page = options.page.unwrap_or(1);
        let per_page = options.per_page.unwrap_or(10);
        let offset = (page - 1) * per_page;

        let products = Self::find_published_products(
            db,
            options.language_code.as_deref(),
            options.category_id,
            options.brand_id,
            Some(per_page as u64),
            Some(offset as u64),
        ).await?;

        let total = Self::count_published_products(
            db,
            options.language_code.as_deref(),
            options.category_id,
            options.brand_id,
        ).await?;

        let products: Vec<crate::models::Product> = products.into_iter().map(|(product, translation_opt)| {
            let translations = if let Some(translation) = translation_opt {
                vec![crate::models::ProductTranslation {
                    id: translation.id,
                    product_id: translation.product_id,
                    language_code: translation.language_code,
                    name: translation.name,
                    description: translation.description,
                    short_description: translation.short_description,
                    meta_title: translation.meta_title,
                    meta_description: translation.meta_description,
                }]
            } else {
                vec![]
            };

            let status = crate::models::PublicationStatus::from_str(&product.status)
                .unwrap_or(crate::models::PublicationStatus::Draft);

            crate::models::Product {
                id: product.id,
                sku: product.sku,
                price: product.price,
                compare_price: product.compare_price,
                cost_price: product.cost_price,
                track_inventory: product.track_inventory,
                weight: product.weight,
                status,
                featured_image_url: product.featured_image_url,
                published_at: product.published_at,
                created_at: product.created_at,
                updated_at: product.updated_at,
                translations,
                category_id: product.category_id,
                brand_id: product.brand_id,
            }
        }).collect();

        Ok(crate::models::PaginatedResponse {
            data: products,
            pagination: crate::models::Pagination {
                page,
                per_page,
                total: total as u64,
                total_pages: ((total as u32 + per_page - 1) / per_page),
            },
        })
    }

    async fn count_published_products(
        db: &DatabaseConnection,
        language_code: Option<&str>,
        category_id: Option<Uuid>,
        brand_id: Option<Uuid>,
    ) -> Result<usize, AppError> {
        let mut query = product::Entity::find()
            .filter(product::Column::Status.eq("published"));
        
        if let Some(cat_id) = category_id {
            query = query.filter(product::Column::CategoryId.eq(cat_id));
        }
        
        if let Some(brand) = brand_id {
            query = query.filter(product::Column::BrandId.eq(brand));
        }
        
        let count = query.count(db).await?;
        Ok(count as usize)
    }
}